package callForPapers;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import sistema.AreaDeInteres;
import sistema.Sistema;
import sistema.Usuario;
import AsignacionDeRevisores.AsignacionDeRevisores;
import AsignacionDeRevisores.AsignacionPorAlta;
import AsignacionDeRevisores.AsignacionPorAreasDeInteres;
import AsignacionDeRevisores.AsignacionPorMayoria;

public class CallForPaperTestCase {
 //prueva
	CallForPaper cfp;
	Calendar fechaTopeDePresentacion;
	Calendar fechaTopeRevision;
	String nombre;
	Set<AreaDeInteres> areasDeInteres;
	Sistema sistema;
	AreaDeInteres areaComputacion;
	AreaDeInteres areaQuimica;
	AreaDeInteres areaFisica;
	HashSet<Usuario> revisores;
	Usuario usuarioQ;
	Usuario usuarioF;
	Usuario usuarioC;
	Usuario usuarioQF;
	Usuario usuarioQFC;
	Paper paperF;
	List<Usuario> usuariosSistema;
	Set<AreaDeInteres> areasUserQ;
	Set<AreaDeInteres> areasUserF;
	Set<AreaDeInteres> areasUserC;
	Set<AreaDeInteres> areasUserQF;
	Set<AreaDeInteres> areasUserQFC;

	@Before
	public void setUp() {

		this.nombre = new String();
		this.fechaTopeDePresentacion = Calendar.getInstance();
		this.fechaTopeRevision = Calendar.getInstance();
		this.areasDeInteres = new HashSet<AreaDeInteres>();
		this.sistema = Sistema.getInstance();
		this.revisores = new HashSet<Usuario>();

		// mock de Area Computacion
		this.areaComputacion = mock(AreaDeInteres.class);
		when(this.areaComputacion.getNombre()).thenReturn("Computacion");

		// mock de Area Quimica
		this.areaQuimica = mock(AreaDeInteres.class);
		when(this.areaQuimica.getNombre()).thenReturn("Quimica");

		// mock de Area Fisica
		this.areaFisica = mock(AreaDeInteres.class);
		when(this.areaFisica.getNombre()).thenReturn("Fisica");

		// mock de un user que tiene area de expertise de quimica
		this.areasUserQ = new HashSet<AreaDeInteres>();
		this.areasUserQ.add(this.areaQuimica);
		this.usuarioQ = mock(Usuario.class);
		when(this.usuarioQ.getAreasDeInteres()).thenReturn(this.areasUserQ);

		// mock de un user que tiene area de expertise de fisica
		this.areasUserF = new HashSet<AreaDeInteres>();
		this.areasUserF.add(this.areaFisica);
		this.usuarioF = mock(Usuario.class);
		when(this.usuarioF.getAreasDeInteres()).thenReturn(this.areasUserF);

		// mock de un user que tiene area de expertise de fisica y quimica
		this.areasUserQF = new HashSet<AreaDeInteres>();
		this.areasUserQF.add(this.areaFisica);
		this.areasUserQF.add(this.areaQuimica);
		this.usuarioQF = mock(Usuario.class);
		when(this.usuarioQF.getAreasDeInteres()).thenReturn(this.areasUserQF);

		// mock de un user que tiene area de expertise de fisica, quimica,
		// computacion
		this.areasUserQFC = new HashSet<AreaDeInteres>();
		this.areasUserQFC.add(this.areaFisica);
		this.areasUserQFC.add(this.areaQuimica);
		this.areasUserQFC.add(this.areaComputacion);
		this.usuarioQFC = mock(Usuario.class);
		when(this.usuarioQFC.getAreasDeInteres()).thenReturn(this.areasUserQFC);

		// mock de un user que tiene area de expertise de computacion
		this.areasUserC = new HashSet<AreaDeInteres>();
		this.areasUserC.add(this.areaComputacion);
		this.usuarioC = mock(Usuario.class);
		when(this.usuarioC.getAreasDeInteres()).thenReturn(this.areasUserC);

		// mock de un sistema
		this.usuariosSistema = new ArrayList<Usuario>();
		this.sistema = mock(Sistema.class);
		when(this.sistema.getUsuarios()).thenReturn(this.usuariosSistema);

		// mock de un paper
		this.paperF = mock(Paper.class);
		when(this.paperF.getAutor()).thenReturn(this.usuarioF);
		when(this.paperF.getAreasDeInteres()).thenReturn(this.areasUserF);

		this.cfp = new CallForPaper(this.usuarioC, this.nombre,
				this.fechaTopeDePresentacion, this.fechaTopeRevision,
				this.areasDeInteres, this.sistema);
		this.cfp.abrirCfp();
		
	}

	@Test
	public void testCallForPaper() {
		// testear que el constructor asigne lo mismo que se le pase por
		// parametros
		// hay dos constructores, la diferencia es que uno se le pasa un area de
		// interes
		// y el otro constructor recibe una lista de areas de interes

		// test con una lista de areas de interes

		Assert.assertEquals(this.usuarioC, this.cfp.getAutor());
		Assert.assertEquals(this.cfp.getNombre(), this.nombre);
		Assert.assertEquals(this.cfp.getFechaTopeDePresentacion(),
				this.fechaTopeDePresentacion);
		Assert.assertEquals(this.cfp.getFechaTopeDeRevision(),
				this.fechaTopeRevision);
		Assert.assertEquals(this.cfp.getAreasDeInteres(), this.areasDeInteres);
		Assert.assertTrue(this.cfp.getPapers().isEmpty());
		Assert.assertTrue(this.cfp.getAsignadorRevisores() instanceof AsignacionDeRevisores);

		// test con una sola area

		this.cfp = new CallForPaper(this.usuarioC, this.nombre,
				this.fechaTopeDePresentacion, this.fechaTopeRevision,
				this.areaComputacion, this.sistema);

		Assert.assertEquals(this.cfp.getNombre(), this.nombre);
		Assert.assertEquals(this.cfp.getFechaTopeDePresentacion(),
				this.fechaTopeDePresentacion);
		Assert.assertEquals(this.cfp.getFechaTopeDeRevision(),
				this.fechaTopeRevision);
		Assert.assertEquals(this.cfp.getSistema(), this.sistema);
		Assert.assertTrue(this.cfp.getAreasDeInteres().contains(
				this.areaComputacion));
		Assert.assertTrue(this.cfp.getPapers().isEmpty());
		Assert.assertTrue(this.cfp.getAsignadorRevisores() instanceof AsignacionDeRevisores);

	}

	@Test
	public void testAgregarAreaDeInteres() {
		// se mira que se agrege en el cfp la area dada

		Assert.assertTrue(this.cfp.getAreasDeInteres().isEmpty());

		this.cfp.agregarAreaDeInteres(this.areaComputacion);

		Assert.assertTrue(this.cfp.getAreasDeInteres().contains(
				this.areaComputacion));

	}

	@Test
	public void testPosiblesRevisoresA() {
		// test con un sistema sin usuarios

		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		Assert.assertTrue(this.cfp.getAreasDeInteres().contains(
				this.areaQuimica));
		Assert.assertTrue(this.cfp.posiblesRevisores().isEmpty());

	}

	/**
	 * test con un sistema con un solo usuario que no coincide
	 */
	@Test
	public void testPosiblesRevisoresB() {
		this.usuariosSistema.add(this.usuarioF); // sistema con un usuario con
													// area fisica
		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		Assert.assertTrue(this.cfp.posiblesRevisores().isEmpty());
	}

	@Test
	public void testPosiblesRevisoresC() {
		// test con solo un usuario que coincide con el area del cfp

		this.usuariosSistema.add(this.usuarioQ); // sistema con un usuario con
													// area fisica
		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioQ));
		Assert.assertEquals(1, this.cfp.posiblesRevisores().size());

	}

	@Test
	public void testPosiblesRevisoresD() {
		// test con 2 usuarios del sistema pero solo 1 coincide con el area del
		// cfp

		this.usuariosSistema.add(this.usuarioQ);
		this.usuariosSistema.add(this.usuarioF);
		this.cfp.agregarAreaDeInteres(this.areaFisica);

		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioF));
		Assert.assertFalse(this.cfp.posiblesRevisores().contains(this.usuarioQ));
		Assert.assertEquals(1, this.cfp.posiblesRevisores().size());

	}

	@Test
	public void testPosiblesRevisoresE() {
		// test con 2 users, 1 tiene 2 areas

		this.usuariosSistema.add(this.usuarioQF);
		this.usuariosSistema.add(this.usuarioQ);

		this.cfp.agregarAreaDeInteres(this.areaFisica);

		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioQF));
		Assert.assertFalse(this.cfp.posiblesRevisores().contains(this.usuarioQ));
		Assert.assertEquals(1, this.cfp.posiblesRevisores().size());

	}

	

	@Test
	public void testPosiblesRevisoresG() {
		// test con un cfp con 2 areas de interes

		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarAreaDeInteres(this.areaQuimica);
		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQ);
		this.usuariosSistema.add(this.usuarioQF);

		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioF));
		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioQ));
		Assert.assertTrue(this.cfp.posiblesRevisores().contains(this.usuarioQF));
		Assert.assertEquals(3, this.cfp.posiblesRevisores().size());

	}

	@Test
	public void testAgregarRevisor() {

		// test donde se agrega un revisor y solo 1 vez, se prueba que no se
		// duplique

		assertTrue(this.cfp.getRevisores().isEmpty());

		this.cfp.agregarRevisor(this.usuarioF);

		assertTrue(this.cfp.getRevisores().contains(this.usuarioF));

		this.cfp.agregarRevisor(this.usuarioF);

		assertTrue(this.cfp.getRevisores().contains(this.usuarioF));
		assertEquals(1, this.cfp.getRevisores().size());

	}

	@Test
	public void testAgregarRevisorB() {

		// test donde no se agrega un revisor si es autor del cfp

		assertTrue(this.cfp.getRevisores().isEmpty());

		this.cfp.agregarRevisor(this.usuarioC);

		assertTrue(this.cfp.getRevisores().isEmpty());

		this.cfp.agregarRevisor(this.usuarioF);

		assertTrue(this.cfp.getRevisores().contains(this.usuarioF));

		assertEquals(1, this.cfp.getRevisores().size());

	}

	@Test
	public void getUsuariosPorInteresA() {
		// test donde ningun revisor tiene el area requerida

		this.cfp.agregarRevisor(this.usuarioF);

		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaQuimica)
				.isEmpty());

	}

	@Test
	public void getUsuariosPorInteresB() {
		// test en donde un revisor coincide con el area seleccionada

		this.cfp.agregarRevisor(this.usuarioF);

		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaFisica)
				.contains(this.usuarioF));
		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaQuimica)
				.isEmpty());

	}

	@Test
	public void getUsuariosPorInteresC() {
		// test donde el cfp no tiene revisores

		this.cfp.agregarAreaDeInteres(this.areaComputacion);

		Assert.assertTrue(this.cfp.getRevisores().isEmpty());
		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaQuimica)
				.isEmpty());

	}

	@Test
	public void getUsuariosPorInteresD() {
		// test donde el cfp no tiene ni areas de interes ni revisores

		Assert.assertTrue(this.cfp.getRevisores().isEmpty());
		Assert.assertTrue(this.cfp.getAreasDeInteres().isEmpty());
		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaQuimica)
				.isEmpty());

	}

	@Test
	public void getUsuariosPorInteresE() {
		// test donde un cfp tiene 2 areas de interes y 2 user

		this.cfp.agregarAreaDeInteres(this.areaQuimica);
		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarRevisor(this.usuarioQF);
		this.cfp.agregarRevisor(this.usuarioF);

		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaFisica)
				.contains(this.usuarioF));
		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaFisica)
				.contains(this.usuarioQF));
		Assert.assertTrue(this.cfp.usuariosPorInteres(this.areaQuimica)
				.contains(this.usuarioQF));
		Assert.assertEquals(2, this.cfp.usuariosPorInteres(this.areaFisica)
				.size());
		Assert.assertEquals(1, this.cfp.usuariosPorInteres(this.areaQuimica)
				.size());

	}

	@Test
	public void ordenarRevisoresPorAfinidad() {
		// test donde 1 usuario que tiene 2 areas en comun al cfp aparece
		// primero que un user con solo 1 area en comun al cfp

		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQF);

		Assert.assertEquals(this.usuarioQF, this.cfp
				.ordenarRevisoresPorAfinidad().get(0));
		Assert.assertEquals(this.usuarioF, this.cfp
				.ordenarRevisoresPorAfinidad().get(1));
		Assert.assertEquals(2, this.cfp.ordenarRevisoresPorAfinidad().size());
	}

	@Test
	public void ordenarRevisoresPorAfinidadB() {
		// test con 3 usuarios
		// uno con 2 areas coincidentes
		// otro con solo 1 area
		// y otro sin coincidencias

		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQF);
		this.usuariosSistema.add(this.usuarioC);

		Assert.assertEquals(this.usuarioQF, this.cfp
				.ordenarRevisoresPorAfinidad().get(0));
		Assert.assertEquals(this.usuarioF, this.cfp
				.ordenarRevisoresPorAfinidad().get(1));
		Assert.assertEquals(2, this.cfp.ordenarRevisoresPorAfinidad().size());

	}

	@Test
	public void ordenarRevisoresPorAfinidadC() {
		// test donde ningun revisor coincide con las areas del cfp

		this.cfp.agregarAreaDeInteres(this.areaComputacion);

		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQF);

		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().isEmpty());

	}

	@Test
	public void ordenarRevisoresPorAfinidadD() {
		// test donde solo 1 revisor coincide con las areas del cfp

		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQF);
		this.usuariosSistema.add(this.usuarioC);

		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().contains(
				this.usuarioQF));
		assertEquals(1, this.cfp.ordenarRevisoresPorAfinidad().size());

	}

	@Test
	public void ordenarRevisoresPorAfinidadE() {

		this.cfp.setAutor(this.usuarioF);
		this.cfp.agregarAreaDeInteres(this.areaComputacion);
		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		this.usuariosSistema.add(this.usuarioQ);
		this.usuariosSistema.add(this.usuarioF);
		this.usuariosSistema.add(this.usuarioQF);
		this.usuariosSistema.add(this.usuarioC);
		this.usuariosSistema.add(this.usuarioQFC);

		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().contains(
				this.usuarioC));
		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().contains(
				this.usuarioQ));
		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().contains(
				this.usuarioQF));
		assertTrue(this.cfp.ordenarRevisoresPorAfinidad().contains(
				this.usuarioQFC));
		assertEquals(this.usuarioQFC, this.cfp.ordenarRevisoresPorAfinidad()
				.get(0));
		assertEquals(4, this.cfp.ordenarRevisoresPorAfinidad().size());

	}



	@Test
	public void soyRevisor() {
		// test con solo un revisor
		this.cfp.agregarRevisor(this.usuarioQ);

		assertTrue(this.cfp.soyRevisor(this.usuarioQ));
		assertFalse(this.cfp.soyRevisor(this.usuarioF));

	}

	@Test
	public void soyRevisorB() {
		// test con un cfp sin ningun revisor

		assertFalse(this.cfp.soyRevisor(this.usuarioC));
		assertFalse(this.cfp.soyRevisor(this.usuarioF));

	}

	@Test
	public void asignadorPorAlta() {

		this.cfp.asignadorPorAlta();

		assertTrue(this.cfp.getAsignadorRevisores() instanceof AsignacionPorAlta);
	}

	@Test
	public void asignadorPorAreasDeInteres() {

		this.cfp.asignadorPorAreasDeInteres();

		assertTrue(this.cfp.getAsignadorRevisores() instanceof AsignacionPorAreasDeInteres);
	}

	@Test
	public void asignadorPorMayoria() {

		this.cfp.asignadorPorMayoria();

		assertTrue(this.cfp.getAsignadorRevisores() instanceof AsignacionPorMayoria);
	}

	@Test
	public void testAgregarPaper() {

		// test donde se prueva que se agrege un paper y solo 1 paper sin
		// duplicados
		this.cfp.setAutor(this.usuarioC);
		this.cfp.agregarAreaDeInteres(this.areaFisica);

		assertTrue(this.cfp.getPapers().isEmpty());

		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().contains(this.paperF));

		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().contains(this.paperF));
		assertEquals(1, this.cfp.getPapers().size());

	}

	@Test
	public void testAgregarPaperB() {

		// test donde se prueva que no se agregue un paper con autor = revisor
		// cfp

		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarRevisor(this.usuarioF);

		assertTrue(this.cfp.getPapers().isEmpty());

		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().isEmpty());

		this.cfp.getRevisores().remove(this.usuarioF);

		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().contains(this.paperF));
		assertEquals(1, this.cfp.getPapers().size());

	}

	@Test
	public void testAgregarPaperC() {

		// test donde se prueva que no se agregue un paper sin area = area del
		// paper

		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		assertTrue(this.cfp.getPapers().isEmpty());

		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().isEmpty());

		this.cfp.agregarAreaDeInteres(this.areaFisica);
		this.cfp.agregarPaper(this.paperF);

		assertTrue(this.cfp.getPapers().contains(this.paperF));
		assertEquals(1, this.cfp.getPapers().size());

	}

	@Test
	public void tieneAreaEnComun() {

		// test donde un usuario tiene area en comun y otro no

		assertTrue(this.cfp.getAreasDeInteres().isEmpty());

		this.cfp.agregarAreaDeInteres(this.areaComputacion);

		assertTrue(this.cfp.tieneAreaEnComun(this.usuarioC));

		assertFalse(this.cfp.tieneAreaEnComun(this.usuarioQ));

	}

	@Test
	public void tieneAreaEnComunB() {

		// test donde un usuario con varias areas tiene area en comun

		assertTrue(this.cfp.getAreasDeInteres().isEmpty());

		this.cfp.agregarAreaDeInteres(this.areaQuimica);

		assertTrue(this.cfp.tieneAreaEnComun(this.usuarioQF));

		assertTrue(this.cfp.tieneAreaEnComun(this.usuarioQ));

	}

	@Test
	public void tieneAreaEnComunC() {

		// test donde 2 usuarios con varias areas tiene area en comun

		assertTrue(this.cfp.getAreasDeInteres().isEmpty());

		this.cfp.agregarAreaDeInteres(this.areaFisica);

		assertTrue(this.cfp.tieneAreaEnComun(this.usuarioQF));

		assertTrue(this.cfp.tieneAreaEnComun(this.usuarioQFC));

	}

	@Test
	public void asignarPapersARevisoresPorAlta() {
		// test donde se prueba que se asignen los revisores POR ALTA
		// en los papaper Correspondientes

		Assert.fail();

	}

	@Test
	public void calcularNotaPapers() {

		// test donde los papers calculan sus notas finales

		Assert.fail();
	}

}
